/**
 * WTF Engine
 *
 * License... etc.
 **
 * XInput API
 * Driver for the Xbox 360 controller
 **
 * Author: Sean Micklethwaite
 **/

#include "input.h"

#ifdef __WM_WIN32__

#include <windows.h>
#include <XInput.h>
#include <limits>

using namespace WtfEngine;

#undef max

void XInputControllerUpdate( int idx, unsigned id, bool bFullUpdate, PXINPUT_GAMEPAD pState, PXINPUT_GAMEPAD pLastState )
{
	unsigned axis = 0;

#define XINPUT_UPDATE_AXIS( _T, _axis, _dz ) \
	{ \
		_T realVal = abs(pState->_axis) < _dz ? 0 : pState->_axis; \
		if( bFullUpdate || realVal != (abs(pLastState->_axis) < _dz ? 0 : pLastState->_axis) ) { \
			Kernel::GetInstance().ProcessEvent( new ControllerAxisEvent( id, axis, float(realVal) / std::numeric_limits<_T>::max() ) ); \
		}; \
		axis++; \
	}

#define XINPUT_UPDATE_BUTTON( _btn ) \
	if( bFullUpdate || (pState->wButtons & _btn) != (pLastState->wButtons & _btn) ) { \
		Kernel::GetInstance().ProcessEvent( new ControllerAxisEvent( id, axis, (pState->wButtons & _btn) ? 1.0 : 0.0 ) ); \
	}; \
	axis++; \


	XINPUT_UPDATE_AXIS( SHORT, sThumbLX, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE );
	XINPUT_UPDATE_AXIS( SHORT, sThumbLY, XINPUT_GAMEPAD_LEFT_THUMB_DEADZONE );
	XINPUT_UPDATE_AXIS( SHORT, sThumbRX, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE );
	XINPUT_UPDATE_AXIS( SHORT, sThumbRY, XINPUT_GAMEPAD_RIGHT_THUMB_DEADZONE );
	XINPUT_UPDATE_AXIS( BYTE, bLeftTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD );
	XINPUT_UPDATE_AXIS( BYTE, bRightTrigger, XINPUT_GAMEPAD_TRIGGER_THRESHOLD );

	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_A );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_B );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_X );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_Y );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_DPAD_UP );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_DPAD_DOWN );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_DPAD_LEFT );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_DPAD_RIGHT );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_BACK );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_START );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_LEFT_SHOULDER );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_RIGHT_SHOULDER );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_LEFT_THUMB );
	XINPUT_UPDATE_BUTTON( XINPUT_GAMEPAD_RIGHT_THUMB );
};
	
	
	

FUNCTOR(XInputUpdate)
	static bool gControllerPresent[] = { false, false, false, false };
	static unsigned gControllerIds[4];
	static XINPUT_STATE gControllerState[4];

	for( int i = 0; i < 4; i++ ) {
		XINPUT_STATE state;
		bool bFullUpdate = false;
		bool bConnected = ERROR_DEVICE_NOT_CONNECTED != XInputGetState( i, &state );

		if( bConnected ) {
			if( !gControllerPresent[i] ) {
				gControllerPresent[i] = true;
				gControllerIds[i] = InputHandler::GetInstance().RegisterController( "Xbox 360 Controller for Windows" );
				bFullUpdate = true;
			};

			if( bFullUpdate || gControllerState[i].dwPacketNumber != state.dwPacketNumber ) {
				XInputControllerUpdate( i, gControllerIds[i], bFullUpdate, &state.Gamepad, &gControllerState[i].Gamepad );
			};

			gControllerState[i] = state;
		} else {
			if( gControllerPresent[i] ) {
				gControllerPresent[i] = false;
				InputHandler::GetInstance().DeregisterController( gControllerIds[i] );
			};
		};
	};
ENDFUNC;

void InitXInput()
{
	Kernel::GetInstance().BindEvent( new FrameStartEvent(), new XInputUpdate() );
}

#endif
